#include <GameEngine/Core/observer.h>


#ifdef GAMEENGINE_NAMESPACES
namespace GameEngine {
#endif

//=========================================================================================

//! Default constructor
ObserverSubject::ObserverSubject() {}

//! Destructor
ObserverSubject::~ObserverSubject() {
	clear_observers();
}

//! Notify all observers that the state of the subject has changed
void ObserverSubject::notify_observers() const {
	for ( std::vector< Observer* >::const_iterator it = observers_.begin(); it != observers_.end(); ++it ) {
		(*it)->notify(this);
	}
}

//! Try to find the observer in the list of observers
/*!
 @param observer The observer to find
 @return True if observer could be found, false otherwise
 */
bool ObserverSubject::find_observer( const Observer* observer ) const {
	for ( std::vector< Observer* >::const_iterator it = observers_.begin(); it != observers_.end(); ++it ) {
		if ( (*it) == observer ) return true;
	}
	return false;
}

//! Try to add the observer to the list of observers
/*!
 @param observer The observer to add
 @return True if observer could be added, false otherwise. An observer may not be added
         if a null pointer was provided, or if observer already observes the subject.
 */
bool ObserverSubject::add_observer( Observer* observer ) {
	// Check observer
	if ( observer == nil ) return false;
	if ( find_observer(observer) == true ) return false;

	// Add the observer and return success
	observers_.push_back(observer);
	return true;
}

//! Try to remove the observer from the list of observers
/*!
 @param observer The observer to remove
 @return True if observer could be removed, false otherwise. An observer may not be
         removed if a null pointer was provided, or if observer does not observe the
		 subject.
 */
bool ObserverSubject::remove_observer( Observer* observer ) {
	if ( observer == nil ) return false;
	for ( std::vector< Observer* >::iterator it = observers_.begin(); it != observers_.end(); ++it ) {
		if ( (*it) != observer ) continue;
		observers_.erase(it);
		return true;
	}
	return false;
}

//! Remove all the observers from the list of observers
void ObserverSubject::clear_observers() {
	while ( observers_.empty() == false ) {
		(*observers_.begin())->disconnect(*this);
	}
}

//=========================================================================================

//! Default constructor
Observer::Observer() {}

//! Destructor
Observer::~Observer() {}

//! Add the observer to the list of observers attached to the subject
/*!
 @param subject The subject to which this observer should be added
 @return True if observer could be added, false otherwise
 */
bool Observer::add_observer(ObserverSubject& subject) {
	return subject.add_observer(this);
}

//! Remove the observer from the list of observers attached to the subject
/*!
 @param subject The subject from which this observer should be removed
 @return True if observer could be removed, false otherwise
 */
bool Observer::remove_observer(ObserverSubject& subject) {
	return subject.remove_observer(this);
}

//=========================================================================================

//! Default constructor
ObserverSingle::ObserverSingle() : subject_(nil), Observer() {}

//! Destructor
ObserverSingle::~ObserverSingle() {
	disconnect_all();
}

//! Connect the observer to the given subject
/*!
 *  If observer was previously connected to another subject, it is first disconnected
 *  from the previous subject.
 *
 @param subject The subject to connect to this observer
 @return True if subject could be connected to this observer, false otherwise
 */
bool ObserverSingle::connect(ObserverSubject& subject) {
	disconnect_all();
	subject_ = &subject;
	return add_observer(subject);
}

//! Disconnect the observer from the subject
/*!
 @param subject The subject to be disconnected. If the subject is not the same as the
                single subject associated to this observer, this function returns false
				and does nothing.
 @return True if subject could be disconnected from the observer, false otherwise
 */
bool ObserverSingle::disconnect(ObserverSubject& subject) {
	if ( &subject != subject_ ) return false;
	return disconnect_all();
}

//! Disconnect the observer from all subjects
/*
 @return True if all subjects could be disconnected from the observer, false otherwise
 */
bool ObserverSingle::disconnect_all() {
	if ( subject_ == nil ) return false;
	bool res = remove_observer(*subject_);
	subject_ = nil;
	return res;
}

//=========================================================================================

//! Default constructor
ObserverMultiple::ObserverMultiple() : Observer() {}

//! Destructor
ObserverMultiple::~ObserverMultiple() {
	disconnect_all();
}

//! Connect the observer to the given subject
/*!
 @param subject The subject to connect to this observer
 @return True if subject could be connected to this observer, false otherwise
 */
bool ObserverMultiple::connect(ObserverSubject& subject) {
	bool res = add_observer(subject);
	if ( res == true ) subjects_.push_back(&subject);
	return res;
}

//! Disconnect the observer from the subject
/*!
 @param subject The subject to be disconnected
 @return True if subject could be disconnected from the observer, false otherwise
 */
bool ObserverMultiple::disconnect(ObserverSubject& subject) {
	bool res = remove_observer(subject);
	for ( std::vector<ObserverSubject*>::iterator it = subjects_.begin(); it != subjects_.end(); ++it ) {
		if ( (*it) != &subject ) continue;
		subjects_.erase(it);
		return res;
	}
	return false;
}

//! Disconnect the observer from all subjects
/*
 @return True if all subjects could be disconnected from the observer, false otherwise
 */
bool ObserverMultiple::disconnect_all() {
	bool res = true;
	while ( subjects_.empty() == false ) {
		res &= disconnect(*(*subjects_.begin()));
	}
	return res;
}

//=========================================================================================

#ifdef GAMEENGINE_NAMESPACES
} // namespace GameEngine
#endif
